package com.turing.common.util;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class BeanMapUtils {

    private static Log logger = LogFactory.getLog(BeanMapUtils.class);

    /**
     * 将JavaBean对象封装到Map集合当中
     *
     * @param bean
     * @return
     * @throws Exception
     */
    public static Map<String, Object> bean2map(Object bean) throws Exception {
        //创建Map集合对象
        Map<String, Object> map = new HashMap<String, Object>();
        //获取对象字节码信息,不要Object的属性
        BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass(), Object.class);
        //获取bean对象中的所有属性
        PropertyDescriptor[] list = beanInfo.getPropertyDescriptors();
        for (PropertyDescriptor pd : list) {
            String key = pd.getName();//获取属性名
            Object value = pd.getReadMethod().invoke(bean);//调用getter()方法,获取内容
            map.put(key, value);//增加到map集合当中
        }
        return map;
    }


    /**
     * 将Map集合中的数据封装到JavaBean对象中
     *
     * @param map       集合
     * @param classType 封装javabean对象
     * @throws Exception
     */
    public static <T> T map2bean(Map<String, Object> map, Class<T> classType) throws Exception {
        //采用反射动态创建对象
        T obj = classType.newInstance();
        //获取对象字节码信息,不要Object的属性
        BeanInfo beanInfo = Introspector.getBeanInfo(classType, Object.class);
        //获取bean对象中的所有属性
        PropertyDescriptor[] list = beanInfo.getPropertyDescriptors();
        for (PropertyDescriptor pd : list) {
            String key = pd.getName();    //获取属性名
            Object value = map.get(key);  //获取属性值
            pd.getWriteMethod().invoke(obj, value);//调用属性setter()方法,设置到javabean对象当中
        }
        return obj;
    }

    /**
     * 两个Bean之间相同属性值复制
     *
     * @param desc 被赋值对象
     * @param src  赋值对象
     * @throws Exception
     */
    public static void copyBeanProperties(Object desc, Object src, SetterMethodReflect setterMethodReflect) {


        Class descClass = desc.getClass();
        Field[] descFields = descClass.getDeclaredFields();
        // 同时获取所有父类属性
        for (Class clazz = descClass; clazz != Object.class; clazz = clazz.getSuperclass()) {
            Field[] superFields = clazz.getDeclaredFields();
            Field[] newFields = new Field[descFields.length + superFields.length];
            System.arraycopy(descFields, 0, newFields, 0, descFields.length);
            System.arraycopy(superFields, 0, newFields, descFields.length, superFields.length);
            descFields = newFields;
        }

        Class srcClass = src.getClass();

//        for (int i=0;i<descFields.length; i++) {
//            System.out.println("**************** in copyBeanProperties, i="+i+", full length is"+descFields.length);
//            Field descField = descFields[i];
//            try {
//                Field srcField = srcClass.getDeclaredField(descField.getName());
//                descField.set(desc, srcField.get(src));
//
//            } catch (NoSuchFieldException e) {
//                e.printStackTrace();
//            } catch (IllegalAccessException e) {
//                e.printStackTrace();
//            } finally {
//            }
//        }

        Arrays.stream(descFields).forEach(descField -> {

            try {

                Field srcField = srcClass.getDeclaredField(descField.getName());

                boolean srcAccessible = srcField.isAccessible();
                boolean descAccessible = descField.isAccessible();

                try {
                    descField.setAccessible(true);
                    srcField.setAccessible(true);
                    descField.set(desc, srcField.get(src));

                    try {
                        setterMethodReflect.invokeSetterMethod(descField.getName(), srcField.get(src));
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }

                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    if (!srcAccessible) {
                        srcField.setAccessible(false);
                    }
                    if (!descAccessible) {
                        descField.setAccessible(false);
                    }
                }


            } catch (NoSuchFieldException e) {
//                e.printStackTrace();
            }


        });
    }
}
